﻿using Batman.Cache.Attributes;
using Batman.Cache.Providers;
using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Batman.Cache
{
    /// <summary>
    /// 缓存拦截器
    /// </summary>
    public class CacheInterceptor : IInterceptor
    {
        public ICachingProvider _cache { get; set; }

        public void Intercept(IInvocation invocation)
        {
            var methodName = $"{invocation.Method.DeclaringType.Namespace}.{invocation.Method.DeclaringType.Name}.{invocation.Method.Name}"; // （被拦截的）方法名

            var cacherAtributes = invocation.Method.GetCustomAttributes<CacherAttribute>().ToList();

            if (cacherAtributes == null || cacherAtributes.Count == 0)
                cacherAtributes = new List<CacherAttribute> { new CacherAttribute(methodName, CacherType.Cache) }; // 默认是缓存结果

            var processed = false; // 是否执行了
            var cacheAttribute = cacherAtributes.FirstOrDefault(t => t.CacherType == CacherType.Cache);
            if (cacheAttribute != null)
            {
                if (cacheAttribute.Id == null) cacheAttribute.Id = methodName;
                if (invocation.Arguments.Length == 0)
                {
                    // 无参缓存函数
                    GetCacheResultOrInvoke(invocation, cacheAttribute);
                    return;
                }
                invocation.Proceed();
                processed = true;
            }

            if (!processed) invocation.Proceed();

            this.CleanCache(cacherAtributes);
        }

        public static readonly string CacheName = "CACHE_INTERCEPTOR";

        /// <summary>
        /// 获取缓存或者执行被拦截的方法（后者是在如果没缓存的情况下执行）
        /// </summary>
        /// <param name="invocation"></param>
        /// <param name="cacherId"></param>
        private void GetCacheResultOrInvoke(IInvocation invocation, CacherAttribute cacher)
        {
            var result = _cache.Get(cacher.Id, () =>
            {
                invocation.Proceed();
                return invocation.ReturnValue;
            }, TimeSpan.FromSeconds(cacher.CacheSeconds == 0 ? 30 : cacher.CacheSeconds)); // 默认30秒

            if (result != null)
                invocation.ReturnValue = Convert.ChangeType(result, invocation.Method.ReturnType);
            else
                invocation.ReturnValue = null;
        }

        /// <summary>
        /// 清除缓存目标
        /// </summary>
        /// <param name="invocation"></param>
        /// <param name="cacher"></param>
        private void CleanCache(List<CacherAttribute> cachers)
        {
            foreach (var cacher in cachers)
            {
                if (cacher.CacherType != CacherType.Clean) continue;

                if (string.IsNullOrEmpty(cacher.TargetId))
                {
                    throw new Exception("CacherAttribute特性在CacherType等于清除缓存时，TargetId不能为空"); // 给开发看的 看看写错了什么
                }

                _cache.Remove(cacher.TargetId);
            }
        }
    }
}
